<template>
  <view class="tui-upload__container">
    <view class="tui-upload-box">
      <view class="tui-image-item" :style="{width:width+'rpx',height:height+'rpx',borderRadius:radius+'rpx'}"
        v-for="(item,index) in imageList" :key="index">
        <image :src="item" class="tui-item-img"
          :style="{width:width+'rpx',height:height+'rpx',borderRadius:radius+'rpx'}" @tap.stop="previewImage(index)"
          mode="aspectFill"></image>
        <view v-if="!forbidDel" class="tui-img-del" :style="{background:getDelColor}" @tap.stop="delImage(index)">
        </view>
        <view v-if="statusArr[index]!=1" class="tui-upload-mask">
        <view class="tui-upload-loading" v-if="statusArr[index]==2"></view>
        <text class="tui-tips">{{statusArr[index]==2?'上传中...':'上传失败'}}</text>
        <view class="tui-mask-btn" v-if="statusArr[index]==3" @tap.stop="reUpLoad(index)" hover-class="tui-btn-hover" :hover-stay-time="150">重新上传</view>
        </view>
      </view>
      <view v-if="isShowAdd" class="tui-upload-add"
        :class="[borderColor!=='transparent'?'tui-upload__border':'tui-upload__unborder']"
        :style="{width:width+'rpx',height:height+'rpx',background:background,borderRadius:radius+'rpx',borderColor:borderColor,borderStyle:borderSytle}"
        @tap="chooseImage">
        <slot>
          <view class="tui-upload-icon tui-icon-plus" :style="{color:addColor,fontSize:addSize+'rpx'}"></view>
        </slot>
      </view>
    </view>
  </view>
</template>

<script>
  export default {
    name: 'tuiUpload',
    emits: ['remove', 'complete', 'reupload'],
    props: {
      //展示图片宽度
      width: {
        type: [Number, String],
        default: 218
      },
      //展示图片高度
      height: {
        type: [Number, String],
        default: 218
      },
      //初始化图片路径
      value: {
        type: Array,
        default () {
          return []
        }
      },
      //2.3.0+
      radius: {
        type: [Number, String],
        default: 0
      },
      //2.3.0+
      background: {
        type: String,
        default: '#F7F7F7'
      },
      //2.3.0+
      borderColor: {
        type: String,
        default: 'transparent'
      },
      //2.3.0+
      //solid、dashed、dotted
      borderSytle: {
        type: String,
        default: 'dashed'
      },
      //2.3.0+
      delColor: {
        type: String,
        default: ''
      },
      //删除图片前是否弹框确认
      delConfirm: {
        type: Boolean,
        default: false
      },
      //禁用删除
      forbidDel: {
        type: Boolean,
        default: false
      },
      //V2.9.6+ 删除图片是否触发 @complete 事件
      delTrigger: {
        type: Boolean,
        default: true
      },
      //2.3.0+
      addColor: {
        type: String,
        default: '#888'
      },
      //2.3.0+
      addSize: {
        type: [Number, String],
        default: 68
      },
      //禁用添加
      forbidAdd: {
        type: Boolean,
        default: false
      },
      //服务器接口地址。当接口地址为空时，直接返回本地图片地址
      serverUrl: {
        type: String,
        default: ""
      },
      //限制数
      limit: {
        type: Number,
        default: 9
      },
      //original 原图，compressed 压缩图，默认二者都有
      sizeType: {
        type: Array,
        default () {
          return ['original', 'compressed']
        }
      },
      //album 从相册选图，camera 使用相机，默认二者都有。如需直接开相机或直接选相册，请只使用一个选项
      sourceType: {
        type: Array,
        default () {
          return ['album', 'camera']
        }
      },
      //可上传图片类型，默认为空，不限制  Array<String> ['jpg','png','gif']
      imageFormat: {
        type: Array,
        default () {
          return []
        }
      },
      //单张图片大小限制 MB 
      size: {
        type: Number,
        default: 4
      },
      //文件对应的key，默认为 file
      fileKeyName: {
        type: String,
        default: "files"
      },
      //HTTP 请求 Header, header 中不能设置 Referer。
      header: {
        type: Object,
        default () {
          return {
            'Authorization': uni.getStorageSync('token')
          }
        }
      },
      //HTTP 请求中其他额外的 form data
      formData: {
        type: Object,
        default () {
          return {}
        }
      },
      //自定义参数
      params: {
        type: [Number, String],
        default: 0
      }
    },
    data() {
      return {
        //图片地址
        imageList: [],
        tempFiles: [],
        //上传状态：1-上传成功 2-上传中 3-上传失败
        statusArr: [],
        //传入回调函数上传
        callUpload: false
      }
    },
    created() {
      this.initImages()
    },
    watch: {
      value(val) {
        if (val) {
          this.initImages()
        }
      }
    },
    computed: {
      isShowAdd() {
        let isShow = true;
        if (this.forbidAdd || (this.limit && this.imageList.length >= this.limit)) {
          isShow = false;
        }
        return isShow
      },
      getDelColor() {
        return this.delColor || (uni && uni.$tui && uni.$tui.color.danger) || '#EB0909';
      }
    },
    methods: {
      initImages() {
        this.statusArr = [];
        this.imageList = [...this.value];
        let tempFiles = []
        for (let item of this.imageList) {
          this.statusArr.push("1")
          tempFiles.push({
            path: item
          })
        }
        this.tempFiles = tempFiles;
      },
      // 重新上传
      reUpLoad(index) {
        this.$set(this.statusArr, index, "2")
        this.$emit('reupload', {
          index
        })
        if (!this.callUpload) {
          this.uploadImage(index, this.imageList[index]).then(() => {
            this.change()
          }).catch(() => {
            this.change()
          })
        }
      },
      /**
       * @param manual 是否手动上传
       **/
      change(manual = false) {
        let status = ~this.statusArr.indexOf("2") ? 2 : 1
        if (status != 2 && ~this.statusArr.indexOf("3")) {
          // 上传失败
          status = 3
        }
        this.$emit('complete', {
          status: status,
          imgArr: this.imageList,
          params: this.params,
          manual: manual
        })
      },
      toast(text) {
        text && uni.showToast({
          title: text,
          icon: "none"
        });
      },
      chooseImage: function() {
        let _this = this;
        uni.chooseImage({
          count: _this.limit - _this.imageList.length,
          sizeType: _this.sizeType,
          sourceType: _this.sourceType,
          success: function(e) {
            let imageArr = [];
            for (let i = 0; i < e.tempFiles.length; i++) {
              let len = _this.imageList.length;
              if (len >= _this.limit) {
                _this.toast(`最多可上传${_this.limit}张图片`);
                break;
              }
              //过滤图片类型
              let path = e.tempFiles[i].path;

              if (_this.imageFormat.length > 0) {
                let format = ""
                // #ifdef H5
                let type = e.tempFiles[i].type;
                format = type.split('/')[1]
                // #endif

                // #ifndef H5
                format = path.split(".")[(path.split(".")).length - 1];
                // #endif

                if (_this.imageFormat.indexOf(format) == -1) {
                  let text = `只能上传 ${_this.imageFormat.join(',')} 格式图片！`
                  _this.toast(text);
                  continue;
                }
              }

              //过滤超出大小限制图片
              let size = e.tempFiles[i].size;

              if (_this.size * 1024 * 1024 < size) {
                let err = `单张图片大小不能超过：${_this.size}MB`
                _this.toast(err);
                continue;
              }
              imageArr.push(path)
              _this.imageList.push(path)
              _this.tempFiles.push(e.tempFiles[i])
              _this.statusArr.push("2")
            }
            _this.change()

            let start = _this.imageList.length - imageArr.length
            for (let j = 0; j < imageArr.length; j++) {
              let index = start + j
              //服务器地址
              if (_this.serverUrl) {
                _this.uploadImage(index, imageArr[j]).then(() => {
                  _this.change()
                }).catch(() => {
                  _this.change()
                })
              } else {
                //无服务器地址则直接返回成功
                _this.$set(_this.statusArr, index, "1")
                _this.change()
              }
            }
          }
        })
      },
      uploadImage: function(index, url, serverUrl) {
        let _this = this;
        return new Promise((resolve, reject) => {
          uni.uploadFile({
            url: this.serverUrl || serverUrl,
            name: this.fileKeyName,
            header: this.header,
            formData: this.formData,
            filePath: url,
            success:res=> {
              let data = JSON.parse(res.data).data[0]
              this.$emit('getFileId',data)
              _this.$set(_this.statusArr, index, "1")
              reject(index)

            },
            fail: function(res) {
              _this.$set(_this.statusArr, index, "3")
              reject(index)
            }
          })
        })

      },
      delImage: function(index) {
        let that = this
        if (this.delConfirm) {
          uni.showModal({
            title: '提示',
            content: '确认删除该图片吗？',
            showCancel: true,
            cancelColor: "#555",
            confirmColor: "#eb0909",
            confirmText: "确定",
            success(res) {
              if (res.confirm) {
                that.imageList.splice(index, 1)
                that.tempFiles.splice(index, 1)
                that.statusArr.splice(index, 1)
                that.$emit("remove", {
                  index: index,
                  params: that.params
                })
                that.delTrigger && that.change()
              }
            }
          })
        } else {
          that.imageList.splice(index, 1)
          that.tempFiles.splice(index, 1)
          that.statusArr.splice(index, 1)
          that.$emit("remove", {
            index: index,
            params: that.params
          })
          that.delTrigger && that.change()
        }
      },
      previewImage: function(index) {
        if (!this.imageList.length) return;
        uni.previewImage({
          current: this.imageList[index],
          loop: true,
          urls: this.imageList
        })
      },
      /**
       * 当属性serverUrl传空时，父级调用该方法一次性上传所有图片
       * @param serverUrl 服务器接口地址
       **/
      uploadAllImage(serverUrl) {
        if (!serverUrl) {
          this.toast('服务器接口地址不能为空！');
          return;
        }
        let imageArr = [...this.imageList]
        const len = imageArr.length
        for (let i = 0; i < len; i++) {
          //如果是服务器地址图片则无需再次上传
          if (imageArr[i].startsWith('https')) {
            continue;
          } else {
            this.$set(this.statusArr, i, "2")
            this.uploadImage(i, imageArr[i], serverUrl).then(() => {
              if (i === len - 1) {
                this.change(true)
              }
            }).catch(() => {
              if (i === len - 1) {
                this.change(true)
              }
            })
          }
        }
      },
      upload(callback, index) {
        // 传入一个返回Promise的文件上传的函数
        //上传状态：1-上传成功 2-上传中 3-上传失败
        this.callUpload = true;
        if (index === undefined || index === null) {
          let urls = [...this.imageList]
          const len = urls.length
          for (let i = 0; i < len; i++) {
            if (urls[i].startsWith('https')) {
              continue;
            } else {
              this.$set(this.statusArr, i, "2")
              if (typeof callback === 'function') {
                callback(this.tempFiles[i]).then(res => {
                  this.$set(this.statusArr, i, '1')
                  this.imageList[i] = res
                  this.change(true)
                }).catch(err => {
                  this.$set(this.statusArr, i, '3')
                })
              }
            }
          }
        } else {
          //如果传入index，则是重新上传时调用
          this.$set(this.statusArr, index, "2")
          if (typeof callback === 'function') {
            callback(this.tempFiles[index]).then(res => {
              this.$set(this.statusArr, index, '1')
              this.imageList[index] = res
              this.change(true)
            }).catch(err => {
              this.$set(this.statusArr, index, '3')
            })
          }
        }
      }
    }
  }
</script>

<style scoped>
  @font-face {
    font-family: 'tuiUpload';
    src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAATcAA0AAAAAByQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAAEwAAAABoAAAAciR52BUdERUYAAASgAAAAHgAAAB4AKQALT1MvMgAAAaAAAABCAAAAVjxvR/tjbWFwAAAB+AAAAEUAAAFK5ibpuGdhc3AAAASYAAAACAAAAAj//wADZ2x5ZgAAAkwAAADXAAABAAmNjcZoZWFkAAABMAAAAC8AAAA2FpiS+WhoZWEAAAFgAAAAHQAAACQH3QOFaG10eAAAAeQAAAARAAAAEgwAACBsb2NhAAACQAAAAAwAAAAMAEoAgG1heHAAAAGAAAAAHwAAACABEgA2bmFtZQAAAyQAAAFJAAACiCnmEVVwb3N0AAAEcAAAACgAAAA6OMUs4HjaY2BkYGAAYo3boY/i+W2+MnCzMIDAzb3qdQj6fwPzf+YGIJeDgQkkCgA/KAtvAHjaY2BkYGBu+N/AEMPCAALM/xkYGVABCwBZ4wNrAAAAeNpjYGRgYGBl0GJgZgABJiDmAkIGhv9gPgMADTABSQB42mNgZGFgnMDAysDA1Ml0hoGBoR9CM75mMGLkAIoysDIzYAUBaa4pDA7PGJ9xMjf8b2CIYW5gaAAKM4LkANt9C+UAAHjaY2GAABYIVmBgAAAA+gAtAAAAeNpjYGBgZoBgGQZGBhBwAfIYwXwWBg0gzQakGRmYnjE+4/z/n4EBQksxSf6GqgcCRjYGOIeRCUgwMaACRoZhDwCiLwmoAAAAAAAAAAAAAAAASgCAeNpdjkFKw0AARf/vkIR0BkPayWRKQZtYY90ohJju2kOIbtz0KD1HVm50UfEmWXoAr9ADOHFARHHzeY//Fx8Ci+FJfIgdJFa4AhgiMshbrCuIsLxhFJZVs+Vl1bT1GddtbXTC3OhohN4dg4BJ3zMJAnccyfm468ZzHXddrH9ZKbHzdf9n/vkY/xv9sPQXgGEvBrHHwst5kTbXLE+YpYVPkxepPmW94W16UbdNJd6f3SAzo5W7m1jaKd+8ZZIvk5nlKw9SK6Wle7BLS3f/bTzQLmfAF2T1NsQAeNp9kD1OAzEQhZ/zByQSQiCoXVEA2vyUKRMp9Ailo0g23pBo1155nUg5AS0VB6DlGByAGyDRcgpelkmTImvt6PObmeexAZzjGwr/3yXuhBWO8ShcwREy4Sr1F+Ea+V24jhY+hRvUf4SbuFUD4RYu1BsdVO2Eu5vSbcsKZxgIV3CKJ+Eq9ZVwjfwqXMcVPoQb1L+EmxjjV7iFa2WpDOFhMEFgnEFjig3jAjEcLJIyBtahOfRmEsxMTzd6ETubOBso71dilwMeaDnngCntPbdmvkon/mDLgdSYbh4FS7YpjS4idCgbXyyc1d2oc7D9nu22tNi/a4E1x+xRDWzU/D3bM9JIbAyvkJI18jK3pBJTj2hrrPG7ZynW814IiU68y/SIx5o0dTr3bmniwOLn8owcfbS5kj33qBw+Y1kIeb/dTsQgil2GP5PYcRkAAAB42mNgYoAALjDJyIAOWMGiTIxMjMxsKak5qSWpbFmZiRmJ+QAmgAUIAAAAAf//AAIAAQAAAAwAAAAWAAAAAgABAAMABAABAAQAAAACAAAAAHjaY2BgYGQAgqtL1DlA9M296nUwGgA+8QYgAAA=) format('woff');
    font-weight: normal;
    font-style: normal;
  }

  .tui-upload-icon {
    font-family: "tuiUpload" !important;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    padding: 10rpx;
  }

  .tui-icon-delete:before {
    content: "\e601";
  }

  .tui-icon-plus:before {
    content: "\e609";
  }

  .tui-upload-box {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
  }

  .tui-upload-add {
    font-weight: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    overflow: hidden;
    box-sizing: border-box;
    /* #ifdef H5 */
    cursor: pointer;
    /* #endif */
  }

  .tui-upload__unborder {
    border-width: 0;
  }

  .tui-upload__border {
    border-width: 1px;
  }

  .tui-image-item {
    position: relative;
    margin-right: 20rpx;
    margin-bottom: 20rpx;
    flex-shrink: 0;
  }

  .tui-item-img {
    display: block;
  }

  .tui-img-del {
    width: 36rpx;
    height: 36rpx;
    position: absolute;
    right: -12rpx;
    top: -12rpx;
    border-radius: 50%;
    color: white;
    font-size: 34rpx;
    z-index: 5;
    /* #ifdef H5 */
    cursor: pointer;
    /* #endif */
  }

  .tui-img-del::before {
    content: '';
    width: 16rpx;
    height: 1px;
    position: absolute;
    left: 10rpx;
    top: 18rpx;
    background-color: #fff;
  }

  .tui-upload-mask {
    width: 100%;
    height: 100%;
    position: absolute;
    left: 0;
    top: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 40rpx 0;
    box-sizing: border-box;
    background-color: rgba(0, 0, 0, 0.6);
    z-index: 3;
  }

  .tui-upload-loading {
    width: 28rpx;
    height: 28rpx;
    border-radius: 50%;
    border: 2px solid;
    border-color: #B2B2B2 #B2B2B2 #B2B2B2 #fff;
    animation: tui-rotate 0.7s linear infinite;
  }

  @keyframes tui-rotate {
    0% {
      transform: rotate(0);
    }

    100% {
      transform: rotate(360deg);
    }
  }

  .tui-tips {
    font-size: 26rpx;
    color: #fff;
  }

  .tui-mask-btn {
    padding: 4rpx 16rpx;
    border-radius: 40rpx;
    text-align: center;
    font-size: 24rpx;
    color: #fff;
    border: 1px solid #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-top: 26rpx;
  }

  .tui-btn-hover {
    opacity: 0.8;
  }
</style>